
# 从qtpy库的QtWidgets模块导入所有组件
from qtpy.QtWidgets import *
# 从qtpy库导入QtCore和QtGui模块，不过QtGui后续未使用，可移除
from qtpy import QtCore, QtGui

# 从src模块导入配置管理器类
from src.ConfigManager import ConfigManager
# 从src.Core.Common模块导入单例装饰器
from src.Core.Common import SingletonDecorator


class CategoryButton(QPushButton):
    """
    自定义的分类按钮类，继承自QPushButton。
    该按钮具有最小高度限制，可勾选且支持自动排他。
    """

    def __init__(self, icon=None, text="test", parent=None):
        """
        初始化分类按钮。

        :param icon: 按钮图标，默认为None
        :param text: 按钮文本，默认为"test"
        :param parent: 父控件，默认为None
        """
        # 调用父类的构造函数
        super(CategoryButton, self).__init__(text, parent)
        # 设置按钮的最小高度为30
        self.setMinimumHeight(30)
        # 设置按钮为可勾选状态
        self.setCheckable(True)
        # 设置按钮支持自动排他
        self.setAutoExclusive(True)


class CategoryWidgetBase(QScrollArea):
    """
    自定义的分类基础窗口类，继承自QScrollArea。
    该窗口支持调整子控件大小，同时提供了初始化默认值、序列化和显示时的处理方法。
    """

    def __init__(self, parent=None):
        """
        初始化分类基础窗口。

        :param parent: 父控件，默认为None
        """
        # 调用父类的构造函数
        super(CategoryWidgetBase, self).__init__(parent)
        # 设置窗口可调整子控件大小
        self.setWidgetResizable(True)

    def initDefaults(self, settings):
        """
        初始化默认值的方法，具体实现由子类完成。

        :param settings: 配置设置对象
        """
        pass

    def serialize(self, settings):
        """
        序列化方法，用于将窗口的设置保存到配置中，具体实现由子类完成。

        :param settings: 配置设置对象
        """
        pass

    def onShow(self, settings):
        """
        窗口显示时的处理方法，具体实现由子类完成。

        :param settings: 配置设置对象
        """
        pass


@SingletonDecorator
class PreferencesWindow(QMainWindow):
    """
    单例模式的偏好设置窗口类，继承自QMainWindow。
    该窗口用于管理应用程序的偏好设置，提供了添加分类、保存设置等功能。
    """

    def __init__(self, parent=None):
        """
        初始化偏好设置窗口。

        :param parent: 父控件，默认为None
        """
        # 调用父类的构造函数
        QMainWindow.__init__(self, parent)
        # 设置窗口大小
        self.resize(600, 400)
        # 设置窗口标题
        self.setWindowTitle("Preferences")
        # 创建中央控件
        self.centralWidget = QWidget(self)
        # 设置中央控件的对象名称
        self.centralWidget.setObjectName("centralWidget")
        # 创建垂直布局并设置给中央控件
        self.verticalLayout = QVBoxLayout(self.centralWidget)
        # 设置布局的边距
        self.verticalLayout.setContentsMargins(1, 1, 1, 1)
        # 设置布局的对象名称
        self.verticalLayout.setObjectName("verticalLayout")
        # 创建分割器
        self.splitter = QSplitter(self.centralWidget)
        # 设置分割器的方向，此处存在类 'Qt' 的未解析的特性引用问题
        self.splitter.setOrientation(QtCore.Qt.Horizontal)
        # 设置分割器的对象名称
        self.splitter.setObjectName("splitter")
        # 创建滚动区域
        self.scrollArea = QScrollArea(self.splitter)
        # 设置滚动区域可调整子控件大小
        self.scrollArea.setWidgetResizable(True)
        # 设置滚动区域的对象名称
        self.scrollArea.setObjectName("scrollArea")
        # 创建滚动区域的内容控件
        self.scrollAreaWidgetContents = QWidget()
        # 设置内容控件的几何位置和大小
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 497, 596))
        # 设置内容控件的对象名称
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        # 设置滚动区域的大小策略，此处存在类 'QSizePolicy' 的未解析的特性引用问题
        self.scrollArea.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred)
        # 创建垂直布局并设置给内容控件
        self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents)
        # 设置布局的边距
        self.verticalLayout_3.setContentsMargins(1, 1, 1, 1)
        # 设置布局的对象名称
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        # 设置布局的间距
        self.verticalLayout_3.setSpacing(2)
        # 创建分类垂直布局
        self.categoriesVerticalLayout = QVBoxLayout()
        # 设置分类垂直布局的对象名称
        self.categoriesVerticalLayout.setObjectName("categoriesLayout")
        # 创建间隔项，此处存在类 'QSizePolicy' 的未解析的特性引用问题
        spacer = QSpacerItem(10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding)
        # 将间隔项添加到分类垂直布局中
        self.categoriesVerticalLayout.addItem(spacer)
        # 将分类垂直布局添加到内容控件的布局中
        self.verticalLayout_3.addLayout(self.categoriesVerticalLayout)
        # 将内容控件设置给滚动区域
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        # 设置滚动区域的最小宽度
        self.scrollArea.setMinimumWidth(200)
        # 创建堆叠窗口
        self.stackedWidget = QStackedWidget(self.splitter)
        # 设置堆叠窗口的对象名称
        self.stackedWidget.setObjectName("stackedWidget")
        # 设置堆叠窗口的大小策略，此处存在类 'QSizePolicy' 的未解析的特性引用问题
        self.stackedWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        # 将分割器添加到中央控件的布局中
        self.verticalLayout.addWidget(self.splitter)
        # 设置中央控件
        self.setCentralWidget(self.centralWidget)
        # 设置分割器的初始大小
        self.splitter.setSizes([150, 450])
        # 存储分类索引的字典
        self._indexes = {}
        # 存储分类按钮的字典
        self.categoryButtons = {}
        # 创建水平布局用于放置按钮
        self.buttonsLay = QHBoxLayout()
        # 创建保存为默认设置的按钮
        pbSavePrefs = QPushButton("SaveAsDefault")
        # 连接按钮的点击信号到保存偏好设置的方法
        pbSavePrefs.clicked.connect(self.savePreferences)
        # 创建保存并关闭的按钮
        pbSaveAndClosePrefs = QPushButton("SaveAndClose")
        # 连接按钮的点击信号到保存并关闭的方法
        pbSaveAndClosePrefs.clicked.connect(self.saveAndClosePrefs)
        # 将保存为默认设置的按钮添加到按钮布局中
        self.buttonsLay.addWidget(pbSavePrefs)
        # 将保存并关闭的按钮添加到按钮布局中
        self.buttonsLay.addWidget(pbSaveAndClosePrefs)
        # 将按钮布局添加到分类垂直布局中
        self.categoriesVerticalLayout.addLayout(self.buttonsLay)

        # 尝试创建默认设置
        self.tryCreateDefaults()

    def selectByName(self, name):
        """
        根据分类名称选择对应的堆叠窗口页面。

        :param name: 分类名称
        """
        if name in self._indexes:
            # 获取分类对应的索引
            index = self._indexes[name][0]
            # 设置堆叠窗口的当前索引
            self.stackedWidget.setCurrentIndex(index)
            # 设置对应的分类按钮为选中状态
            self.categoryButtons[index].setChecked(True)

    def tryCreateDefaults(self):
        """
        尝试创建默认设置。
        从配置管理器中获取设置，并根据设置初始化各个分类窗口的默认值。
        """
        # 从配置管理器中获取偏好设置
        settings = ConfigManager().getSettings("PREFS")
        # 获取设置中的所有分组
        groups = settings.childGroups()
        for name, indexWidget in self._indexes.items():
            index, widget = indexWidget
            bInitDefaults = False
            if name not in groups:
                # 如果分组不存在，则需要初始化默认值
                bInitDefaults = True
            # 进入指定分组
            settings.beginGroup(name)
            if bInitDefaults:
                # 调用窗口的初始化默认值方法
                widget.initDefaults(settings)
            # 退出当前分组
            settings.endGroup()
        # 同步设置
        settings.sync()

    def showEvent(self, event):
        """
        窗口显示事件处理方法。
        当窗口显示时，从配置管理器中获取设置，并调用各个分类窗口的显示处理方法。

        :param event: 显示事件对象
        """
        # 从配置管理器中获取偏好设置
        settings = ConfigManager().getSettings("PREFS")
        # 获取设置中的所有分组
        groups = settings.childGroups()
        for name, indexWidget in self._indexes.items():
            index, widget = indexWidget
            # 进入指定分组
            settings.beginGroup(name)
            if name not in groups:
                # 如果分组不存在，则调用窗口的初始化默认值方法
                widget.initDefaults(settings)
            # 调用窗口的显示处理方法
            widget.onShow(settings)
            # 退出当前分组
            settings.endGroup()

    def saveAndClosePrefs(self):
        """
        保存偏好设置并关闭窗口。
        """
        self.savePreferences()
        self.close()

    def savePreferences(self):
        """
        保存偏好设置。
        遍历所有分类窗口，调用其序列化方法将设置保存到配置管理器中。
        """
        # 从配置管理器中获取偏好设置
        settings = ConfigManager().getSettings("PREFS")
        for name, indexWidget in self._indexes.items():
            index, widget = indexWidget
            # 进入指定分组
            settings.beginGroup(name)
            # 调用窗口的序列化方法
            widget.serialize(settings)
            # 退出当前分组
            settings.endGroup()
        # 同步设置
        settings.sync()

    def addCategory(self, name, widget):
        """
        向偏好设置窗口中添加一个分类。

        :param name: 分类名称
        :param widget: 分类对应的窗口控件
        """
        # 创建分类按钮
        categoryButton = CategoryButton(text=name)
        # 将分类按钮插入到分类垂直布局中
        self.categoriesVerticalLayout.insertWidget(
            self.categoriesVerticalLayout.count() - 2, categoryButton
        )
        # 设置分类窗口的大小策略，此处存在类 'QSizePolicy' 的未解析的特性引用问题
        widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        # 将分类窗口添加到堆叠窗口中，并获取其索引
        index = self.stackedWidget.addWidget(widget)
        # 存储分类名称和对应的索引、窗口控件
        self._indexes[name] = (index, widget)
        # 存储索引和对应的分类按钮
        self.categoryButtons[index] = categoryButton
        # 连接分类按钮的点击信号到切换分类内容的方法
        categoryButton.clicked.connect(
            lambda checked=False, idx=index: self.switchCategoryContent(idx)
        )

    def switchCategoryContent(self, index):
        """
        切换堆叠窗口的当前页面，并切换对应的分类按钮状态。

        :param index: 要切换到的页面索引
        """
        # 设置堆叠窗口的当前索引
        self.stackedWidget.setCurrentIndex(index)
        # 切换对应的分类按钮状态
        self.categoryButtons[index].toggle()